<html>
 <head>
  <script type="text/javascript" src="js/fileread.js"></script>
  <script type="text/javascript" src="js/log.js"></script>
  <script type="text/javascript" src="js/gpu.js"></script>
  <script type="text/javascript" src="js/mmu.js"></script>
  <script type="text/javascript" src="js/key.js"></script>
  <script type="text/javascript" src="js/timer.js"></script>
  <script type="text/javascript" src="js/z80.js"></script>
  <script type="text/javascript" src="js/tabs.js"></script>
  <script type="text/javascript" src="js/xhr.js"></script>
  <style type="text/css">
* { margin:0; padding: 0; }
body { padding: 5px; background-color: black; color: white; font-family:Arial, Helvetica, sans-serif; font-size:0.82em; }
#out { height:144px; width:160px; border: 1px solid white; float:left; margin:0 5px 0 0; }
#msg { margin: 5px; font-family: sans-serif; font-size: 0.82em; }
div.tab { height:124px; width:320px; border:1px solid white; margin:20px 5px 5px 165px; overflow:auto; }
ul.ops { float:left; list-style:none inside; }
ul.ops li { cursor:pointer; }
table#reg { font-size:11px; font-family:Lucida Console, Bitstream Vera Sans Mono, monospace; line-height:1em; }
table#reg td.regname { text-align:right; padding-left:1em; }
ul.tablist { list-style: none inside; position:relative; bottom:-1px; }
ul.tablist li { display: block; float: left; background: #444; border-top: 1px solid #444; border-bottom: 1px solid white; padding: 3px 0.5em; margin-right: 2px; cursor: pointer; font-size:9px; }
ul.tablist li.tab_hi { border-left: 1px solid white; border-right: 1px solid white; border-top: 1px solid white; border-bottom: 1px solid black; background: black; }
p#op_load { margin-left: 165px; }
input { background:black; color:white; border:1px solid white; width:5em; }
input#file { width:10em; }
div#tilectrl { float:left; margin:1em;}
div#tilepixels { width:96px; height:96px; border:1px solid white; float:left; margin:1em; }
div#tilepixels div { width:12px; height:12px; float:left; }
div.subcanv { width:160px; float:left; }
p.fps { float:right; text-align:right; }
  </style>
 </head>
 <body>
  <div id="out">
   <canvas id="screen" width="160" height="144">
   </canvas>
  </div>
  <ul class="tablist" id="tablist_debug">
   <li rel="tab_message">Messages</li>
   <li rel="tab_registers">Debugger</li>
   <li rel="tab_tile">Tile View</li>
  </ul>
  <div class="tab" id="tab_message"><div id="msg"></div></div>
  <div class="tab" id="tab_registers">
   <table id="reg">
    <tr>
     <td class="regname">A</td><td class="reg" rel="a"></td>
     <td class="regname">LCDC</td><td class="io" rel="40"></td>
     <td class="regname">JOYP</td><td class="io" rel="00"></td>
    </tr>
    <tr>
     <td class="regname">B</td><td class="reg" rel="b"></td>
     <td class="regname">STAT</td><td class="io" rel="41"></td>
    </tr>
    <tr>
     <td class="regname">C</td><td class="reg" rel="c"></td>
     <td class="regname">SCY</td><td class="io" rel="42"></td>
     <td class="regname">DIV</td><td class="io" rel="04"></td>
    </tr>
    <tr>
     <td class="regname">D</td><td class=reg" rel="d"></td>
     <td class="regname">SCX</td><td class="io" rel="43"></td>
     <td class="regname">TIMA</td><td class="io" rel="05"></td>
    </tr>
    <tr>
     <td class="regname">E</td><td class="reg" rel="e"></td>
     <td class="regname">LY</td><td class="io" rel="44"></td>
     <td class="regname">TMA</td><td class="io" rel="06"></td>
    </tr>
    <tr>
     <td class="regname">HL</td><td class="reg" rel="hl"></td>
     <td class="regname">LYC</td><td class="io" rel="45"></td>
     <td class="regname">TCA</td><td class="io" rel="07"></td>
    </tr>
    <tr>
     <td class="regname">PC</td><td class="reg" rel="pc"></td>
     <td class="regname">IE</td><td class="io" rel="ff"></td>
    </tr>
    <tr>
     <td class="regname">SP</td><td class="reg" rel="sp"></td>
     <td class="regname">IF</td><td class="io" rel="0f"></td>
    </tr>
    <tr>
     <td class="regname">F</td><td class="reg" rel="f"></td>
    </tr>
   </table>
  </div>
  <div class="tab" id="tab_tile">
   <div id="tilectrl">
    <input type="text" id="tilenum" value="0"><br>
    <ul class="ops">
     <li id="tileprev">Prev</li>
     <li id="tilenext">Next</li>
    </ul>
   </div>
   <div id="tilepixels"></div>
  </div>
  <div class="subcanv">
   <ul class="ops">
    <li id="op_reset">Reset</li>
    <li id="op_run">Run</li>
    <li id="op_step">Step</li>
   </ul>
   <p class="fps"><span id="fps">0</span> fps</p>
  </div>
  <p id="op_load">
   Load <input type="text" id="file" value="/tests/tetris.gb">
   Break at <input type="text" id="breakpoint">
  </p>
  <script type="text/javascript">
jsGB = {
  run_interval: 0,
  trace: '',

  frame: function() {
    var fclock = Z80._clock.m+17556;
    var brk = document.getElementById('breakpoint').value;
    var t0 = new Date();
    do {
      if(Z80._halt) Z80._r.m=1;
      else
      {
      //  Z80._r.r = (Z80._r.r+1) & 127;
        Z80._map[MMU.rb(Z80._r.pc++)]();
        Z80._r.pc &= 65535;
      }
      if(Z80._r.ime && MMU._ie && MMU._if)
      {
        Z80._halt=0; Z80._r.ime=0;
	var ifired = MMU._ie & MMU._if;
        if(ifired&1) { MMU._if &= 0xFE; Z80._ops.RST40(); }
        else if(ifired&2) { MMU._if &= 0xFD; Z80._ops.RST48(); }
        else if(ifired&4) { MMU._if &= 0xFB; Z80._ops.RST50(); }
        else if(ifired&8) { MMU._if &= 0xF7; Z80._ops.RST58(); }
        else if(ifired&16) { MMU._if &= 0xEF; Z80._ops.RST60(); }
	else { Z80._r.ime=1; }
      }
      //jsGB.dbgtrace();
      Z80._clock.m += Z80._r.m;
      GPU.checkline();
      TIMER.inc();
      if((brk && parseInt(brk,16)==Z80._r.pc) || Z80._stop)
      {
        jsGB.pause();
        break;
      }
    } while(Z80._clock.m < fclock);

    var t1 = new Date();
    document.getElementById('fps').innerHTML=Math.round(10000/(t1-t0))/10;
  },
  
  reset: function() {
    LOG.reset(); GPU.reset(); MMU.reset(); Z80.reset(); KEY.reset(); TIMER.reset();
    Z80._r.pc=0x100;MMU._inbios=0;Z80._r.sp=0xFFFE;Z80._r.hl=0x014D;Z80._r.c=0x13;Z80._r.e=0xD8;Z80._r.a=1;
    MMU.load(document.getElementById('file').value);
  
    document.getElementById('op_reset').onclick=jsGB.reset;
    document.getElementById('op_run').onclick=jsGB.run;
    document.getElementById('op_run').innerHTML='Run';
    document.getElementById('op_step').onclick=jsGB.step;
  
    document.getElementById('tilepixels').innerHTML='';
    var tp = document.createElement('div');
    var x;
    for(var i=0; i<64; i++)
    {
      document.getElementById('tilepixels').appendChild(tp);
      tp = tp.cloneNode(false);
    }
    document.getElementById('tilenum').onupdate=jsGB.dbgtile();
    document.getElementById('tileprev').onclick=function(){
      var t=parseInt(document.getElementById('tilenum').value); t--; if(t<0) t=383;
      document.getElementById('tilenum').value=t.toString();
      jsGB.dbgtile();
    };
    document.getElementById('tilenext').onclick=function(){
      var t=parseInt(document.getElementById('tilenum').value); t++; if(t>383) t=0;
      document.getElementById('tilenum').value=t.toString();
      jsGB.dbgtile();
    };

    jsGB.dbgupdate();
    jsGB.dbgtile();
    jsGB.trace = '';
    tabMagic.init();
    jsGB.pause();
  
    LOG.out('MAIN', 'Reset.');
  },
  
  run: function() {
    Z80._stop = 0;
    jsGB.run_interval = setInterval(jsGB.frame,1);
    document.getElementById('op_run').innerHTML = 'Pause';
    document.getElementById('op_run').onclick = jsGB.pause;
  },
  
  pause: function() {
    clearInterval(jsGB.run_interval);
    Z80._stop = 1;
    jsGB.dbgupdate();
  
    document.getElementById('op_run').innerHTML = 'Run';
    document.getElementById('op_run').onclick = jsGB.run;

    //XHR.connect('/log.php', {trace:jsGB.trace}, {success:function(x){}});
  },

  dbgupdate: function() {
    var t = document.getElementById('reg').getElementsByTagName('td');
    var x,j,k;
    for(var i=0; i<t.length; i++)
    {
      if(t[i].className=='reg')
      {
        switch(t[i].getAttribute('rel'))
	{
	  case 'a': case 'b': case 'c': case 'd': case 'e':
	    eval('x=Z80._r.'+t[i].getAttribute('rel')+'.toString(16);if(x.length==1)x="0"+x;');
	    break;
	  case 'pc': case 'sp':
	    eval('x=Z80._r.'+t[i].getAttribute('rel')+'.toString(16);if(x.length<4){p="";for(j=4;j>x.length;j--)p+="0";x=p+x;}');
	    break;
	  case 'hl':
	    k = (Z80._r.h<<8)+Z80._r.l;
	    x = k.toString(16); if(x.length<4){p="";for(j=4;j>x.length;j--)p+="0";x=p+x;}
	    break;
	  case 'f':
	    x = (Z80._r.f>>4).toString(2);if(x.length<4){p="";for(j=4;j>x.length;j--)p+="0";x=p+x;}
	    break;
	}
	t[i].innerHTML = x;
      }
      else if(t[i].className=='io')
      {
        j = parseInt(t[i].getAttribute('rel'),16);
	x = MMU.rb(0xFF00+j).toString(16);
	if(typeof(x) != 'undefined')
	{
	  if(x.length==1) x='0'+x;
	  t[i].innerHTML = x;
	}
      }
    }
  },
  
  dbgtrace: function() {
    var a = Z80._r.a.toString(16); if(a.length==1) a='0'+a;
    var b = Z80._r.b.toString(16); if(b.length==1) b='0'+b;
    var c = Z80._r.c.toString(16); if(c.length==1) c='0'+c;
    var d = Z80._r.d.toString(16); if(d.length==1) d='0'+d;
    var e = Z80._r.e.toString(16); if(e.length==1) e='0'+e;
    var f = Z80._r.f.toString(16); if(f.length==1) f='0'+f;
    var h = Z80._r.h.toString(16); if(h.length==1) h='0'+h;
    var l = Z80._r.l.toString(16); if(l.length==1) l='0'+l;
    var pc = Z80._r.pc.toString(16); if(pc.length<4) { p=''; for(i=4;i>pc.length;i--) p+='0'; pc=p+pc; }
    var sp = Z80._r.sp.toString(16); if(sp.length<4) { p=''; for(i=4;i>sp.length;i--) p+='0'; sp=p+sp; }
    jsGB.trace +=
      ("A"+a+"/B"+b+"/C"+c+"/D"+d+"/E"+e+"/F"+f+"/H"+h+"/L"+l+"/PC"+pc+"/SP"+sp+"\n");
  },

  dbgtile: function() {
    var tn = parseInt(document.getElementById('tilenum').value);
    var t = GPU._tilemap[tn];
    var c = ['#ffffff','#c0c0c0','#606060','#000000'];
    var d = document.getElementById('tilepixels').getElementsByTagName('div');

    for(var y=0;y<8;y++)
      for(var x=0;x<8;x++)
        d[y*8+x].style.backgroundColor=c[t[y][x]];
  },

  step: function() {
    if(Z80._r.ime && MMU._ie && MMU._if)
    {
      Z80._halt=0; Z80._r.ime=0;
      if((MMU._ie&1) && (MMU._if&1))
      {
        MMU._if &= 0xFE; Z80._ops.RST40();
      }
    }
    else
    {
      if(Z80._halt) { Z80._r.m=1; }
      else
      {
        Z80._r.r = (Z80._r.r+1) & 127;
        Z80._map[MMU.rb(Z80._r.pc++)]();
        Z80._r.pc &= 65535;
      }
    }
    Z80._clock.m += Z80._r.m; Z80._clock.t += (Z80._r.m*4);
    GPU.checkline();
    if(Z80._stop)
    {
      jsGB.pause();
    }
    jsGB.dbgupdate();
  }
};

window.onload = jsGB.reset;
window.onkeydown = KEY.keydown;
window.onkeyup = KEY.keyup;
  </script>
 </body>
</html>
